/*!  \file  ImplementationFactory.h
 *	  \brief  
 * 
 *   \details   details
 *   \author    Yordan Madzhunkov German Research School for Simulation Sciences GmbH
 *   \version   1.0
 *   \date      04/24/2013 10:47:18 AM
 *   \copyright GNU Public License.
 */
#pragma once
#include <time.h>
#include "macros.h"
#include "CompileTime.h"
class Implementation : public CompileTime { // The product of out factory
public:
	Implementation(const char * name_, const unsigned int group_);
	virtual ~Implementation() {
		if (name        ) delete [] name			; name 			= 0; 
	};
	 
	virtual bool setNumberOfThreads(const int nThreads_){ 
		nThreads = canBeParallel ? nThreads_ : 1;
		return canBeParallel;
	};
	void setName 				(const char * val);
	void setSanityCheck(bool val) { sanity = val; tested = true; };

	const char * getName         () const { return name; };
	unsigned int getGroup        () const { return group; };
	unsigned int getMemUsage     () const { return extraMemory; };
	bool         getSanityCheck  () const { return sanity; };
	bool         isTested        () const { return tested; };
	int          getMaxUsedMemory() const { return maxMemory; };
	bool         computaionSuccessed() const { return success; };
	int 			 getNThreads     () const { return nThreads; };
	bool 			 getCanBeParallel() const { return canBeParallel; };
	template <typename T> 
	bool allocate(T * RS & ptr, const int n) {
		success &= ptr == 0;;
		ptr = new T[n]; extraMemory += sizeof(T) * n;
		maxMemory = maxMemory < extraMemory ? extraMemory : maxMemory;
		success &= ptr != 0;;
		return ptr != 0;
	}
	template <typename T> void delocate(T * RS & p) { if (p) delete [] p; p = 0;}
private:
	char * name;
	const unsigned int group; 
protected:
	inline void reset() {success = true; maxMemory = 0; extraMemory = 0;	}
	unsigned int extraMemory;
	unsigned int maxMemory;
	int nThreads; // how many threads this implementations uses.
	bool sanity;
	bool tested;
	bool success; // true if last computaiton was successful
	bool canBeParallel; // true, if this implementation can be parallel
};

enum Group { BiReductionGroup = 0,	BiSVDGroup  = 1, FormSVDGroup = 2,
					 FullSVDGroup = 3, TriEigenGroup = 4	};


class Map;
class Matrix;
class Benchmark;
class UnitTest;
class ImplementationFactory { 
public:
	enum { MaxGroups = 5 };
	static ImplementationFactory * instance();

	Implementation * getImplementation(const char * name) const;

	Implementation * first(unsigned int group) const;
	Implementation * next (unsigned int group) const;
	

	unsigned int getNGroups         () const {return nGroups;};
	unsigned int getMaxGroups       () const {return MaxGroups;};
	unsigned int getNImplementations() const;

	Implementation * getImplementation(unsigned int group, unsigned int ID);	
	virtual ~ImplementationFactory();
	void printAllRegistered() const;
	template <class T> friend class RegisterImplementation;
	void printGroupPlotFiles() const;
private:
	void   registerImplementation(Implementation & newImplementation );
	void deregisterImplementation(Implementation & newImplementation );
	void printGroupPlotFiles(unsigned int group) const;
	ImplementationFactory();
	static ImplementationFactory * myInstance;
	unsigned int nGroups;
	unsigned int nImplementations;
	Map             * groups    [MaxGroups];
	Implementation ** bluePrints[MaxGroups];
	Benchmark      ** benchmarks[MaxGroups];
	UnitTest       ** unitTests [MaxGroups];
};

// This class registers given implementation in Implementation Factory
// note that only this class can registerImplementation
#include <iostream>
using namespace std;
template <class T>
class RegisterImplementation {
public:
	RegisterImplementation( T & newImplementation) {
		data = &newImplementation;
		Implementation& toReg = dynamic_cast<Implementation&>(newImplementation);
		ImplementationFactory::instance()->registerImplementation(toReg);
	}
	virtual ~RegisterImplementation() {
		ImplementationFactory::instance()->deregisterImplementation(
			dynamic_cast<Implementation&>(*data));
		if (data) delete data; data = 0; 
	};
private:
	RegisterImplementation();
	DISALLOW_COPY_AND_ASSIGN(RegisterImplementation);
	T * data;	
};

#define REGISTER(x) RegisterImplementation< x > reg_##x(*(new x()));

